package com.zdy.boot.upload.core.scan;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.zdy.boot.upload.core.job.handler.AbstractJobHandler;
import com.zdy.boot.upload.domain.VideoEntity;
import com.zdy.boot.upload.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author sdy
 * @description
 * @date 2025/2/9
 */
@Slf4j
@Component
public class ScanVideoJob extends AbstractJobHandler {

    @Value("${seal.video.split}")
    private boolean splitFlag;
    @Value("${seal.video.maxUpSize}")
    private int maxSplitSize;
    @Value("${seal.video.url}")
    private String videoUrl;
    @Value("${seal.video.path}")
    private String videoPath;
    @Value("${seal.video.bakPath}")
    private String videoBakPath;
    @Value("${seal.video.type}")
    private String fileType;

    @Override
    public void executeJob(String param) {
        try {
            handle();
        } catch (Exception ex) {
            log.error("上传视频异常", ex);
        }
    }

    private void handle() {
        log.info("开始处理视频文件");
        // 扫描文件
        try(Stream<Path> pathStream = Files.walk(Paths.get(videoPath))) {
            pathStream.filter(p -> FileUtils.filter(p, fileType)).forEach(this::handleVideo);
        } catch (IOException e) {
            log.error("读取本地文件异常：" + e.getMessage(), e);
            throw new RuntimeException(e.getMessage());
        }
        log.info("结束处理视频文件");
    }

    private void handleVideo(Path path) {
        // 校验参数
        if (!StringUtils.hasText(videoUrl)) {
            log.info("请求地址未配置，停止上传");
            return;
        }
        if (path == null) {
            log.info("请求文件为空，停止上传");
            return;
        }
        log.info("视频上传地址：{}", videoUrl);
        if (splitFlag) {
            splitUpload(path);
        } else {
            upload(path);
        }
    }

    /**
     * 不做断点续传，逐个上传
     * @param path 文件列表
     */
    private void upload(Path path) {
        log.info("视频上传参数：{}", path.getFileName().toString());
        // 请求
        HttpRequest request = HttpRequest.post(videoUrl);
        // 文件
        request.form("file", path.toFile());
        // 请求
        try (HttpResponse response = request.execute()) {
            // 结果
            log.info("上传视频结果：{}", response.body());
            if (response.isOk()) {
                deleteAndBak(path);
            }
        } catch (Exception ex) {
            log.info("上传视频结果异常：{}", ex.getMessage(), ex);
            path = null;
        }
    }

    /**
     * 支持断点续传，需要服务端执行组合（需要本地记录断点任务，未完全测试)
     * @param path 文件列表
     */
    private void splitUpload(Path path) {
        try {
            List<VideoEntity> videoEntities = new ArrayList<>();
            // 切分视频
            splitFile(path, videoEntities);
            // 上传文件
            if (!CollectionUtils.isEmpty(videoEntities)) {
                videoEntities.forEach(v -> HttpUtil.post(videoUrl, JSONUtil.toJsonStr(v)));
            }
            // 添加删除列表
            deleteAndBak(path);
        } catch (Exception ex) {
            log.info("上传视频结果异常：{}", ex.getMessage(), ex);
        }
    }

    private void splitFile(Path path, List<VideoEntity> videoEntities) {
        File file = path.toFile();
        try (RandomAccessFile raf = new RandomAccessFile(file, "r")){
            long offset = 0, fileLength = file.length();
            String fileName = file.getName();
            int partNum = 1;
            // 切分
            while (offset < fileLength) {
                int len = (int) Math.min(maxSplitSize, fileLength - offset);
                byte[] buffer = new byte[len];
                // 读取数据
                raf.seek(offset);
                raf.readFully(buffer);
                // 创建文件块
                videoEntities.add(new VideoEntity(fileName, partNum, buffer, offset + len >= fileLength));
                // 重置偏移量
                offset += len;
                partNum++;
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
            throw new RuntimeException(ex.getMessage());
        }
    }

    private void deleteAndBak(Path source) {
        try {
            // 目标路径
            Path targetPath = Paths.get(videoBakPath);
            // 确保路标路径存在
            Files.createDirectories(targetPath);
            // 移动
            Path target = targetPath.resolve(source.getFileName());
            Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
            // 删除
            Files.deleteIfExists(source);
        } catch (Exception e) {
            source = null;
            log.error("删除视频文件异常", e);
        }
    }

}
